<?php
namespace Twoir\System\Support;

/**
 * 帝国模型表Trait
 *
 * cighsen02 qq 365182575
 * time: 2022.09.29
 * update: 2025.02.19
*/
trait ModelTrait
{
    /**
     * 字段默认值类型
     *
     * @return array
    */
    public static function fieldType(): array
    {
        $default = array_replace(static::MYDEFAULT['index'], static::MYDEFAULT['pri'], static::MYDEFAULT['sec']);

        // 字段类型
        array_walk($default, function(&$value){

            $value = null === $value || 0 === $value ? 'int' : 'string';

        });

        return $default;
    }

    /**
	 * 分组表单名
	 *
	 * @param string $type
	 * @return array
	*/
	private static function parseTable(string $type = ''): array
	{
	    $index 	= 'doc' === $type ? 'doc_index' : 'index';
		$pre	= in_array($type, ['doc', 'check']) ? $type . '_' : '';

		return [
		    'index' => static::MPREFIX . static::MODEL . static::TABLE[$index],
		    'pri'   => static::MPREFIX . static::MODEL . static::TABLE[$pre . 'pri'],
		    'sec'   => static::MPREFIX . static::MODEL . static::TABLE[$pre . 'sec'],
		];
	}

	/**
	 * 统计审核数量
	 *
	 * @param array $where
	 * @param string $field
	 * @param string $type empty|doc|check
	 * @return int
	*/
	public static function getCount(array $where = [], string $field = '', string $type = ''): int
    {
        global $empire, $dbtbpre;

        ['where' => $str_where] = static::parseWhere($where, false);

		$table = static::parseTable($type);

		//位置表位置
		$index = 'all' === $field ? 'index' : 'pri';

	 	$sql = sprintf('SELECT count(*) as total FROM %s%s', $dbtbpre . $table[$index], $str_where);

        return (int)$empire->gettotal($sql);
    }

    /**
	 * 统计待审核数
	 *
	 * @param array $where
	 * @param string $field
	 * @return int
	 */
    public static function getCheckCount(array $where = [], string $field = ''): int
    {
        return static::getCount($where, $field, 'check');
    }

    /**
	 * 存档
	 *
	 * @param array $where
	 * @param string $field
	 * @return int
	*/
    public static function getDocCount(array $where = [], string $field = ''): int
    {
        return static::getCount($where, $field, 'doc');
    }

	/**
	 * 拉取一条数据
	 *
	 * @param array $where 键值数组 $field 值数组
	 * @param array $field
	 * @param string $groupby
	 * @param string $orderby
	 * @param string $type
	 * @return array
	*/
	public static function getOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', string $type = ''): array
	{
		$res = static::getList($where, $field, $orderby, $groupby, 1, 0, 0, $type);

	    return $res ? array_values($res)[0] : [];
	}

	/**
	 * 待审核
	 *
	 * @param array $where 键值数组 $field 值数组
	 * @param array $field
	 * @param string $groupby
	 * @param string $orderby
	 * @return array
	*/
	public static function getCheckOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = ''): array
	{
	    $res = static::getList($where, $field, $orderby, $groupby, 1, 0, 0, 'check');

	    return $res ? array_values($res)[0] : [];
	}

	/**
	 * 存档
	 *
	 * @param array $where 键值数组 $field 值数组
	 * @param array $field
	 * @param string $groupby
	 * @param string $orderby
	 * @return array
	 */
	public static function getDocOne(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = ''): array
	{
	    $res = static::getList($where, $field, $orderby, $groupby, 1, 0, 0, 'doc');

	    return $res ? array_values($res)[0] : [];
	}

	/**
	 * 待审核表
	 *
	 * @param array $where 键值数组 $field 值数组
	 * @param array $field
	 * @param string $groupby
	 * @param string $orderby
	 * @param int $limit
	 * @param int $pagesize
	 * @param int $page
	 * @return array
	*/
	public static function getCheckList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0): array
	{
	    return static::getList($where, $field, $orderby, $groupby, $limit, $pagesize, $page, 'check');
	}

	/**
	 * 存档
	 *
	 * @param array $where 键值数组 $field 值数组
	 * @param array $field
	 * @param string $groupby
	 * @param string $orderby
	 * @param int $limit
	 * @param int $pagesize
	 * @param int $page
	 * @return array
	*/
	public static function getDocList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0): array
	{
	    return static::getList($where, $field, $orderby, $groupby, $limit, $pagesize, $page, 'doc');
	}

	/**
	 * 读取多条(列表)
	 *
	 * @param array $where
	 * @param array $field
	 * @param string $groupby
	 * @param string $orderby
	 * @param int $limit
	 * @param int $pagesize
	 * @param int $page
	 * @param string $type
	 * @return array
	 */
	 public static function getList(array $where = [], array $field = ['*'], string $groupby = '', string $orderby = '', int $limit = 0, int $pagesize = 10, int $page = 0, string $type = ''): array
	 {
        global $empire, $dbtbpre;

		['where' => $str_where, 'data' => $data] = static::parseWhere($where, false);
		// 确定表位置正式表、待审核表、存档表
		$tables 	= static::parseTable($type);
		$get_field  = [];
		$arr_where 	= [
			'index'		=> [],
			'pri'		=> [],
			'sec'		=> []
		];

		if (['*'] !== $field && count($data) > 0) {

			$where_field	= array_keys($data);
			$arr_where		= [
				'index'	=> array_filter(static::FIELD['index'], fn($item) => in_array($item, $where_field)),
				'pri'	=> array_filter(static::FIELD['pri'], fn($item) => in_array($item, $where_field)),
				'sec'	=> array_filter(static::FIELD['sec'], fn($item) => in_array($item, $where_field))
			];
		}

		$str_group	= null === $groupby || '' === $groupby ? '' : ' GROUP BY ' . $groupby;
		$str_order	= null === $orderby || '' === $orderby ? '' : ' ORDER BY ' . $orderby;
        $str_limit 	= 0 === $limit && 0 === $pagesize ? '' : ($limit > 0 ? ' LIMIT ' . $limit : ' LIMIT ' . ($page * $pagesize) . ',' . $pagesize);

        $option = [];
        foreach($tables as $t => $table) {

            $get_field  = ['*'] === $field ? static::FIELD[$t] : array_intersect(static::FIELD[$t], $field);
            $count      = count($get_field);

			if (['*'] === $field
			|| $count > 2
			|| (2 === $count && 'pri' === $t)
			|| (1 === $count && 'pri' === $t && (['classid'] === $get_field || ['id'] === $get_field))
			|| (count($arr_where[$t]) > 0 && ['id'] !== $arr_where[$t] && ['classid'] !== $arr_where[$t] && !(['classid','id'] !== $arr_where[$t] || ['id','classid'] !== $arr_where[$t])) ) {

				$option[$t] = [
				    'field'     => $get_field,
				    'table'     => $dbtbpre . $table,
				    'where'     => $str_where,
					'group'		=> $str_group,
				    'order'     => $str_order,
				    'limit'     => $str_limit
				];
			}
        }

        // 待返回数据
        $res = [];
        // 主表
        if (isset($option['pri']) && !isset($option['sec'])) {

            $sql = sprintf('SELECT %s FROM %s%s%s%s%s',
				'`' . implode('`,`', $option['pri']['field']) . '`',
				$option['pri']['table'],
				$option['pri']['where'],
				$option['pri']['group'],
				$option['pri']['order'],
				$option['pri']['limit']
			);
        }

        // 副表, 注意 分表不设置
        elseif (!isset($option['pri']) && isset($option['sec'])) {

            $sql = sprintf('SELECT %s FROM %s%s%s%s%s',
				'`' . implode('`,`', $option['sec']['field']) . '`',
				'' === $type ? $option['sec']['table'] . ('' === $type ? static::$stb : '') : $option['sec']['table'],
				$option['sec']['where'],
				$option['sec']['group'],
				$option['sec']['order'],
				$option['sec']['limit']
			);
        }

        // 并表
        else{

			$sql = sprintf(
				'SELECT %s FROM `%s` AS a INNER JOIN `%s` AS b ON %s%s%s%s%s',
				'a.`' . implode('`,a.`', $option['pri']['field']) . '`' . ',b.`' . implode('`,b.`', array_diff($option['sec']['field'], ['classid', 'id'])) . '`',
				$option['pri']['table'],
				'' === $type ? $option['sec']['table'] . ('' === $type ? static::$stb : '') : $option['sec']['table'],
				'a.`' . static::PRIMARY . '` = b.`' . static::PRIMARY . '`',
				count($data) > 0 ? self::addPrefix($option['pri']['where']) : '',
				$option['pri']['group'] ? self::addPrefix($option['pri']['group']) : '',
				$option['pri']['order'] ? self::addPrefix($option['pri']['order']) : '',
				$option['pri']['limit']
			);
        }

		$sql = $empire->query($sql);

		if(0 < $empire->num1($sql)){

		    while($r = $empire->fetch($sql)){
		        // 去数字键
		        $res[] = array_filter($r, fn($key) => is_string($key), ARRAY_FILTER_USE_KEY);
		    }
		}

        return $res;
	}

    /**
     * 插入审核表
	 *
     * @param array $add
     * @return int
    */
    public static function insertCheck(array $add = []): int
    {
        return static::insert($add, 'check');
    }


    /**
     * 插入存档表
	 *
     * @param array $add
     * @return int
    */
    public static function insertDoc(array $add = []): int
    {
        return static::insert($add, 'doc');
    }

    /**
     * 插入交易信息
	 *
     * @param array $add
	 * @param string $type
     * @return int 0为失败，成功返回当前id
    */
	public static function insert(array $add = [], string $type = ''): int
	{
		global $empire, $dbtbpre;

        //确定表位置
        $tables = static::parseTable($type);

        $insert = static::MYDEFAULT;
		array_walk($insert, function(&$value, $key) use($add) {

			//分组
			$value  = array_filter($value, fn($item) => in_array($item, static::FIELD[$key]), ARRAY_FILTER_USE_KEY);

			$add    = array_filter($add, fn($item) => in_array($item, static::FIELD[$key]), ARRAY_FILTER_USE_KEY);
			//更新
			$value 	= array_replace($value, $add);
		});

        //移除id, 自动增长
        if (!isset($add['id'])) unset($insert['index']['id']);

        //确定副表位置
        if ($insert['pri']['stb'] > 0 && static::$stb !== $insert['pri']['stb']) static::$stb = $insert['pri']['stb'];

        //引索表插入
        $data       = self::parseInsert($insert['index']);
        $sql        = sprintf('INSERT INTO %s(%s) VALUES(%s)', $dbtbpre . $tables['index'], $data['key'], $data['value']);
        $sqlindex   = $empire->query($sql);

		$insert['pri']['id'] = $insert['sec']['id'] = $lastid = $empire->lastid();

		//主表
		$data   = static::parseInsert($insert['pri']);
		$sql    = sprintf('INSERT INTO %s(%s) VALUES(%s)', $dbtbpre . $tables['pri'], $data['key'], $data['value']);
		$sqlpri = $empire->query($sql);

		//副表
		$data   = self::parseInsert($insert['sec']);
		$sql    = sprintf('INSERT INTO %s(%s) VALUES(%s)', $dbtbpre . $tables['sec'] . ('' === $type ? static::$stb : ''), $data['key'], $data['value']);
		$sqlsec = $empire->query($sql);

		return $sqlindex && $sqlpri && $sqlsec ? $lastid : 0;
	}

	/**
	 * 更新待审核表
	 *
	 * @param array $where
	 * @param array $add
	 * @param int $limit
	 * @return bool
	*/
	public static function updateCheck(array $where = [], array $add = [], int $limit = 0): bool
	{
	    return static::update($where, $add, $limit, 'check');
	}

	/**
	 * 更新存档表
	 *
	 * @param array $where
	 * @param array $add
	 * @param int $limit
	 * @return bool
	*/
	public static function updateDoc(array $where = [], array $add = [], int $limit = 0): bool
	{
	    return static::update($where, $add, $limit, 'doc');
	}

	/**
     * 更新
	 *
	 * @param array $where
	 * @param array $add
	 * @param int $limit
	 * @param string $type
	 * @return bool
     **/
    public static function update(array $where = [], array $add = [], int $limit = 0, string $type = ''): bool
    {
        global $empire, $dbtbpre;

         //去null值, 0值或负值不能去，
        $add = array_filter($add, fn($item) => null !== $item);

        //找出要新字段进行更新
        $field = array_keys($add);

		['where' => $str_where] = static::parseWhere($where, false);

        //确定表位置
        $tables = static::parseTable($type);
        $limit  = $limit ? ' LIMIT ' . $limit : '';

        $sqlindex = $sqlpri = $sqlsec = false;

        //更新引索表
        $update = array_intersect(static::FIELD['index'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s SET %s%s%s', $dbtbpre . $tables['index'], static::parseUpdate($put_data), $str_where, $limit);
            $sqlindex   = $empire->query($sql);
        }

        //更新主表
        $update = array_intersect(static::FIELD['pri'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE `%s` SET %s%s%s', $dbtbpre . $tables['pri'], static::parseUpdate($put_data), $str_where, $limit);
            $sqlpri     = $empire->query($sql);
        }

        //更新副表
        $update = array_intersect(static::FIELD['sec'], $field);
        if (!empty($update)) {

            $put_data   = array_filter($add, fn($item) => in_array($item, $update), ARRAY_FILTER_USE_KEY);
            $sql        = sprintf('UPDATE %s SET %s%s%s', $dbtbpre . $tables['sec'] . ('' === $type ? static::$stb : ''), static::parseUpdate($put_data), $str_where, $limit);
            $sqlsec     = $empire->query($sql);
        }

        return false !== $sqlindex || false !== $sqlpri || false !== $sqlsec;
    }

	/**
	 * 联表名增加前缀
	 *
	 * @param string $str
	 * @return string
	 */
	private static function addPrefix(string $str): string
	{
		foreach (static::FIELD['pri'] as $field) {

			if (str_contains($str, '`' . $field . '`')) $str = str_replace('`' . $field . '`', 'a.`' . $field . '`', $str);
		}

		foreach (static::FIELD['sec'] as $field) {

			if (!in_array($field, ['classid','id']) && str_contains($str, '`' . $field . '`')) $str = str_replace('`' . $field . '`', 'b.`' . $field . '`', $str);
		}

		return $str;
	}
}
